home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’97 / NewsTicker / source code / Internet Code / OTPollEndPoint.cp < prev    next >
Text File  |  1997-06-14  |  16KB  |  636 lines

  1. /*------------------------------------------------------------------------------
  2. #
  3. #    NewsTicker, my Hack for 1997
  4. #
  5. #    OTPollEndPoint.cp    -    A general OT Endpoint class., designed to be derived.
  6. #
  7. #        After you open the connection, calling its DoIdle routine will check for
  8. #        any incoming data, and if found, call a derived method (broken into
  9. #        lines, if desired).  Also, when the connection is broken, it will call a
  10. #        derived shutdown method.
  11. #
  12. #        Not to be treated as a great example of OT programming, but it works.
  13. #
  14. ------------------------------------------------------------------------------*/
  15.  
  16. #include "OTPollEndPoint.h"
  17. #include "Idler.h"
  18.  
  19. typedef struct LookupNameReplyHeader
  20. {
  21.     unsigned short        muiAddrLen;
  22.     unsigned short        muiNameLen;
  23. } LookupNameReplyHeader;
  24.  
  25. const    OTTimeout    kOTResolveTimeout =    40;    // 40 seconds
  26.  
  27. // Just create the class, don't do anything now
  28. OTPollEndPoint::OTPollEndPoint (void)
  29. {
  30.     mfCancelled = false;
  31.     mfDataWaiting = false;
  32.     mfNeedsDisconnecting = false;
  33.     mfLostConnection = false;
  34.     mfDataInIsText = true;
  35.     mulResolveTimeout = kOTResolveTimeout;
  36.     mulResolveTimeout *= 1000L;        // Convert to milliseconds
  37.     mpEndpoint = nil;
  38.     mfBytesReceived = 0;
  39. }
  40.  
  41. // Destructor
  42. OTPollEndPoint::~OTPollEndPoint (void)
  43. {
  44.     OSErr        iErr = Close (nil);
  45. }
  46.  
  47. #pragma push
  48. #pragma segment Main
  49. // Returns true if the OTPollEndPoint::Cancel() has been called since the endpoint was opened, OR
  50. // if the connection has been broken (by the other end disconnecting, for example)
  51. // In keeping with the Connection class cancelling is an immediate, synchronous operation. Cancelling
  52. // is expected to cause all further operations to fail immediately, thus eventually exiting out of the
  53. // operation.
  54. Boolean OTPollEndPoint::EndPointValid (void) const
  55. {
  56.     return (mpEndpoint != nil && !mfCancelled);
  57. }
  58.  
  59.  
  60. OSErr OTPollEndPoint::DestroyEndPoint (void)
  61. {
  62.     OSErr        iErr = noErr;
  63.     
  64.     if (mpEndpoint != nil)
  65.     {
  66.         (void) OTSetSynchronous (mpEndpoint);        // Guaranteed to succeed as per OT docs
  67.         OTRemoveNotifier (mpEndpoint);
  68.         iErr = OTCloseProvider(mpEndpoint);
  69.         mpEndpoint = nil;
  70.         mfDataWaiting = false;
  71.     }
  72.     return iErr;
  73. }
  74.  
  75. void OTPollEndPoint::CheckOTError (OTResult lError)
  76. {
  77.     if (lError == kOTLookErr)
  78.     {
  79.         if ((mpMapperRef == nil)&&(mpEndpoint != nil))
  80.         {
  81.             OSErr            iDummy;
  82.             OTResult        lResult;
  83.             
  84.             switch (OTLook (mpEndpoint))
  85.             {
  86.                 case T_ORDREL:
  87.                     lResult = OTRcvOrderlyDisconnect (mpEndpoint);
  88.                     iDummy = DestroyEndPoint ();
  89.                     break;
  90.                 case T_DISCONNECT:
  91.                     lResult = OTRcvDisconnect (mpEndpoint, nil);
  92.                     iDummy = DestroyEndPoint ();
  93.                     break;
  94.             }
  95.         }
  96.     }
  97. }
  98. #pragma pop
  99.  
  100.  
  101. OSErr OTPollEndPoint::OpenEndpoint(const char *protocol)
  102. {
  103.     OSStatus            err;
  104.  
  105.     mfDataWaiting = false;
  106.     
  107.     mpEndpoint = OTOpenEndpoint (OTCreateConfiguration(protocol), 0, nil, &err);
  108.     CheckOTError (err);
  109.     if (err != noErr)
  110.         mpEndpoint = nil;
  111.     return err;
  112. }
  113.  
  114. #define USE_INET_RESOLVER
  115.  
  116. OSErr OTPollEndPoint::ResolveAddress (Idler& oIdler, const char* pszHost, InetHost& lHostsIPAddress)
  117. {
  118. #ifdef USE_INET_RESOLVER
  119.     OSErr                iResult;
  120.     OSStatus            lErr;
  121.     
  122.     mpMapperRef = nil;
  123.  
  124.     lErr = OTInetStringToHost ((char*) pszHost, &lHostsIPAddress);
  125.     // If host name wasn't an IP address (www.xxx.yyy.zzz) then we must resolve it
  126.     if (lErr != noErr)
  127.     {
  128.         InetHostInfo    sHostInfo;
  129.         
  130.         miAsyncErr = noErr;
  131.         meState = keOpeningMapper;
  132.         
  133.         lErr = OTAsyncOpenInternetServices (kDefaultInternetServicesPath, 0, Notifier, this);
  134.         CheckOTError (lErr);
  135.         if (lErr == noErr)
  136.         {
  137.             do {
  138.                 oIdler.YieldTime ();
  139.             } while (meState == keOpeningMapper && !mfCancelled);
  140.             
  141.             iResult = miAsyncErr;
  142.             CheckOTError (iResult);
  143.             if (iResult == noErr)
  144.             {
  145.                 miAsyncErr = noErr;
  146.                 meState = keResolving;
  147.                 iResult = OTInetStringToAddress ((InetSvcRef) mpMapperRef, (char*) pszHost, &sHostInfo);
  148.                 CheckOTError (iResult);
  149.                 if (iResult == noErr)
  150.                 {
  151.                     do {
  152.                         oIdler.YieldTime ();
  153.                     } while (meState == keResolving && !mfCancelled);
  154.                     
  155.                     iResult = miAsyncErr;
  156.                     if (iResult == noErr && !mfCancelled)
  157.                         lHostsIPAddress = sHostInfo.addrs [0];
  158.                 }
  159.                 if (iResult == noErr)
  160.                     iResult = OTCloseProvider (mpMapperRef);
  161.                 else
  162.                     (void) OTCloseProvider (mpMapperRef);
  163.                 mpMapperRef = nil;
  164.             }
  165.         }
  166.         else
  167.             iResult = lErr;
  168.     }
  169.     else
  170.         iResult = noErr;
  171.     return iResult;
  172. #else
  173.     OSErr        iErr;
  174.     OSStatus    lResult;
  175.     
  176.     mpMapperRef = nil;
  177.     miAsyncErr = noErr;
  178.     meState = keOpeningMapper;
  179.     oIdler.YieldTime ();
  180. #ifdef ASYNC_MAPPER_OPEN
  181.     lResult = OTAsyncOpenMapper (OTCreateConfiguration (kDNRName), 0, &Notifier, this);
  182. #else
  183.     mpMapperRef = OTOpenMapper (OTCreateConfiguration (kDNRName), 0, &lResult);
  184. #endif // ASYNC_MAPPER_OPEN
  185.     CheckOTError (lResult);
  186.     if (lResult == kOTNoError)
  187.     {
  188. #ifdef ASYNC_MAPPER_OPEN
  189.         do {
  190.             oIdler.YieldTime ();
  191.         } while (meState == keOpeningMapper && !mfCancelled);    // Note we aren't dealing with mpEndpoint
  192.         iErr = miAsyncErr;
  193.         CheckOTError (iErr);
  194. #else
  195.         iErr = OTInstallNotifier (mpMapperRef, &Notifier, this);
  196.         {
  197.             OSErr    iIgnore = OTSetAsynchronous (mpMapperRef);        // Guaranteed to succeed as per OT docs
  198.         }
  199. #endif // ASYNC_MAPPER_OPEN
  200.         if (iErr == noErr && !mfCancelled)
  201.         {
  202.             OSErr                    iCloseErr;
  203.             short                    iReplyBufferLen;
  204.             char*                    pReplyBuffer;
  205.             
  206.             iReplyBufferLen = sizeof (LookupNameReplyHeader) + sizeof (InetAddress) + strlen (pszHost) + 5;
  207.             pReplyBuffer = new char [iReplyBufferLen];
  208.             if (pReplyBuffer != nil)
  209.             {
  210.                 size_t                iiHostLen = strlen (pszHost);
  211.                 TLookupRequest        oRequest    =    {
  212.                                                             {    iiHostLen,    iiHostLen,    (UInt8*) pszHost    },
  213.                                                             {    0,    0,    nil    },
  214.                                                             1,                            // Max # of addresses returned by resolver
  215.                                                             mulResolveTimeout
  216.                                                         };
  217.                 TLookupReply        oReply    =    {
  218.                                                             {    iReplyBufferLen,    iReplyBufferLen,    (UInt8*) pReplyBuffer    },
  219.                                                             1
  220.                                                         };
  221.                 
  222.                 miAsyncErr = noErr;
  223.                 meState = keResolving;
  224.                 oIdler.YieldTime ();
  225.                 lResult = OTLookupName (mpMapperRef, &oRequest, &oReply);
  226.                 CheckOTError (lResult);
  227.                 if (lResult == kOTNoError && !mfCancelled)    // Note we aren't dealing with mpEndpoint
  228.                 {
  229.                     do {
  230.                         oIdler.YieldTime ();
  231.                     } while (meState == keResolving && !mfCancelled);    // Note we aren't dealing with mpEndpoint
  232.                     iErr = miAsyncErr;
  233.                     CheckOTError (iErr);
  234.                     if (iErr == noErr && !mfCancelled)            // Note we aren't dealing with mpEndpoint
  235.                     {
  236.                         InetAddress*    psResolvedAddress;
  237.                         
  238.                         psResolvedAddress = (InetAddress*) (pReplyBuffer + sizeof (LookupNameReplyHeader));
  239.                         lHostsIPAddress = psResolvedAddress->fHost;
  240.                     }
  241.                 }
  242.                 else
  243.                     iErr = lResult;
  244.                 delete [] pReplyBuffer;
  245.             }
  246.             else
  247.                 iErr = memFullErr;
  248.             
  249.             if (mpMapperRef != nil)
  250.             {
  251.                 iCloseErr = OTCloseProvider (mpMapperRef);
  252.                 mpMapperRef = nil;
  253.                 CheckOTError (iCloseErr);
  254.                 if (iErr == noErr)
  255.                     iErr = iCloseErr;
  256.                 oIdler.YieldTime ();
  257.             }
  258.         }
  259.         
  260.         if (mfCancelled)
  261.             iErr = keOTPAborted;
  262.     }
  263.     else
  264.         iErr = lResult;
  265.     return iErr;
  266. #endif // USE_INET_RESOLVER
  267. }
  268.  
  269.  
  270. // Put call back in main segment to avoid having the segment unloaded and causing typical callback problems
  271. // if the segment is unloaded when the callback is called
  272. #pragma push
  273. #pragma segment Main
  274.  
  275. // *** Called at deferred task time. ***
  276. pascal void OTPollEndPoint::Notifier(void* contextPtr, OTEventCode code, OTResult result, void* cookie)
  277. {
  278.     OTPollEndPoint*    thisPoint = (OTPollEndPoint*) contextPtr;
  279.     
  280.     thisPoint->HandleNotify (code, result, cookie);
  281. }
  282.  
  283.  
  284. // *** Called at deferred task time. ***
  285. void OTPollEndPoint::HandleNotify( OTEventCode code, OTResult result, void* cookie)
  286. {
  287.     OTResult    lResult;
  288.     
  289.     switch (code)
  290.     {
  291.         case T_BINDCOMPLETE:
  292.             miAsyncErr = result;
  293.             meState = keBindingDone;
  294.             break;
  295.         case T_CONNECT:
  296.             miAsyncErr = result;
  297.             if (result == kOTNoError)
  298.             {
  299.                 if (mpEndpoint != nil)
  300.                 {
  301.                     lResult = OTRcvConnect (mpEndpoint, nil);
  302.                     CheckOTError (lResult);
  303.                 }
  304.                 meState = keConnectingDone;
  305.             }
  306.             else
  307.                 meState = keBailingOut;
  308.             break;
  309.         case T_DISCONNECT:
  310.             miAsyncErr = result;
  311.             meState = keDisconnecting;
  312.             // the endpoint may have been shut down by close call so check for nil
  313.             if (mpEndpoint != nil)
  314.             {
  315.                 lResult = OTRcvDisconnect (mpEndpoint, nil);
  316.                 CheckOTError (lResult);
  317.                 DestroyEndPoint ();
  318.             }
  319.             meState = keDisconnectingDone;
  320.             mfLostConnection = true;
  321.             break;
  322.         case T_ORDREL:
  323.             miAsyncErr = result;
  324.             meState = keOrderlyDisconnecting;
  325.             // the endpoint may have been shut down by close call so check for nil
  326.             if (mpEndpoint != nil)
  327.             {
  328.                 OSErr        iDummy;
  329.                 
  330.                 lResult = OTRcvOrderlyDisconnect (mpEndpoint);
  331.                 CheckOTError (lResult);
  332.                 if (OTGetEndpointState(mpEndpoint)!=T_IDLE)
  333.                 {
  334.                     OTSndOrderlyDisconnect(mpEndpoint);
  335.                 }
  336.                 iDummy = DestroyEndPoint ();
  337.             }
  338.             meState = keOrderlyDisconnectingDone;
  339.             mfLostConnection = true;
  340.             break;
  341.         case T_DATA:
  342.             mfDataWaiting = true;
  343.             break;
  344.         case T_DNRSTRINGTOADDRCOMPLETE:
  345.         case T_LKUPNAMECOMPLETE:
  346.             meState = keResolvingDone;
  347.             miAsyncErr = result;
  348.             break;
  349.         case T_OPENCOMPLETE:
  350.             meState = keOpeningMapperDone;
  351.             miAsyncErr = result;
  352.             if (result == kOTNoError)
  353.                 mpMapperRef = (MapperRef) cookie;
  354.             break;
  355.     }
  356. }
  357.  
  358. #pragma pop
  359.  
  360.  
  361. OSErr OTPollEndPoint::Open (Idler& oIdler, const char *address, short port)
  362. {
  363.     OSErr        err;
  364.     
  365.     mfNeedsDisconnecting = false;
  366.     mfCancelled = false;
  367.     err = OpenEndpoint (kTCPName);
  368.     if (err == noErr && EndPointValid ())
  369.     {
  370.         err = OTInstallNotifier (mpEndpoint, &Notifier, this);
  371.         CheckOTError (err);
  372.         // make it asynchronous
  373.         {
  374.             OSErr        iIgnored = OTSetAsynchronous (mpEndpoint);        // Guaranteed to succeed as per OT docs
  375.         }
  376.         if (err == noErr && EndPointValid ())
  377.         {
  378.             mfDestAddress.inet.fAddressType = AF_INET;
  379.             mfDestAddress.inet.fPort = port;
  380.             err = ResolveAddress (oIdler, address, mfDestAddress.inet.fHost);
  381.             if (err == noErr && EndPointValid ())
  382.             {
  383.                 InetAddress    inAddress;
  384.                 TBind            inBind = { { sizeof(InetAddress), sizeof(InetAddress), (UInt8*) &inAddress }, 0 };
  385.  
  386.                 inAddress.fAddressType = AF_INET;
  387.                 inAddress.fPort = 0;
  388.                 inAddress.fHost = 0;
  389.                 miAsyncErr = noErr;
  390.                 meState = keBinding;
  391.                 err = OTBind (mpEndpoint, &inBind, nil);
  392.                 CheckOTError (err);
  393.                 if (err == kOTNoError)
  394.                 {
  395.                     // Use "do" loop so YieldTime is called no matter what
  396.                     do {
  397.                         oIdler.YieldTime ();
  398.                     } while (meState == keBinding && EndPointValid ());
  399.                     if (!EndPointValid ())
  400.                         err = keOTPEPaborted;
  401.                     else
  402.                     {
  403.                         err = miAsyncErr;
  404.                         CheckOTError (err);
  405.                     }
  406.                     
  407.                     if (err == noErr && EndPointValid ())
  408.                     {
  409.                         TCall    sConnectionParms    =    {
  410.                                                             { sizeof(mfDestAddress), sizeof(mfDestAddress), (UInt8*) &mfDestAddress },
  411.                                                             { 0, 0, NULL },
  412.                                                             { 0, 0, NULL },
  413.                                                             };
  414.  
  415.                         miAsyncErr = noErr;
  416.                         meState = keConnecting;
  417.                         err = OTConnect (mpEndpoint, &sConnectionParms, nil);
  418.                         if (err == kOTNoError || err == kOTNoDataErr)
  419.                         {
  420.                             // Use "do" loop so YieldTime is called no matter what
  421.                             do {
  422.                                 oIdler.YieldTime ();
  423.                             } while (meState == keConnecting && EndPointValid ());
  424.                             if (!EndPointValid ())
  425.                                 err = keOTPEPaborted;
  426.                             else
  427.                             {
  428.                                 err = miAsyncErr;
  429.                                 if (err == noErr)
  430.                                     mfNeedsDisconnecting = true;
  431.                                 else
  432.                                     CheckOTError (err);
  433.                             }
  434.                             // OTRcvConnect() is done in HandleNotify()
  435.                         }
  436.                     }
  437.                 }
  438.             }
  439.         }
  440.         
  441.         // If the open fails we must close the endpoint that we know at this point was successfully opened
  442.         // If endpoint is not valid (mpEndPoint is nil or endpoint has been cancelled) it's benign to call Close()
  443.         if (err != noErr || !EndPointValid ())
  444.         {
  445.             OSErr    iDummy = Close (&oIdler);
  446.         }
  447.     }
  448.         
  449.     return err;
  450. }
  451.  
  452.  
  453. OSErr OTPollEndPoint::Close (Idler* poIdler)
  454. {
  455.     OSErr        iResult = noErr;
  456.     
  457.     // Need to close the endpoint if Cancel() was called so we check for mpEndpoint != nil instead of EndPointValid ()
  458.     if (mpEndpoint != nil)
  459.     {
  460.         OSErr        iDummy;
  461.         
  462.         if (mfNeedsDisconnecting)
  463.         {
  464.             miAsyncErr = noErr;
  465.             meState = keOrderlyDisconnecting;
  466.             iResult = OTSndOrderlyDisconnect (mpEndpoint);
  467.             CheckOTError (iResult);
  468.         }
  469.         
  470.         iDummy = DestroyEndPoint ();
  471.  
  472.         if (poIdler != nil)
  473.             poIdler->YieldTime ();
  474.     }
  475.     return iResult;
  476. }
  477.  
  478.  
  479. void OTPollEndPoint::Cancel (void)
  480. {
  481.     mfCancelled = true;
  482. }
  483.  
  484.  
  485. OSErr OTPollEndPoint::SendData (Idler& oIdler, const void *pData, long lSize)
  486. {
  487.     long        lBytesSent = 0;
  488.     OSStatus    lResult = noErr;
  489.     
  490.     while (EndPointValid ())
  491.     {
  492.         lResult = OTSnd (mpEndpoint, (void*) (((UInt8*) pData) + lBytesSent), lSize, 0);
  493.         
  494.         if (lResult >= 0)
  495.         {
  496.             lBytesSent += lResult;
  497.             if (lBytesSent >= lSize)
  498.             {
  499.                 lResult = kOTNoError;
  500.                 break;
  501.             }
  502.         }
  503.         else if (lResult != kOTFlowErr)
  504.         {
  505.             CheckOTError (lResult);
  506.             break;
  507.         }
  508.         // else no bytes were sent - we'll just try sending again
  509.         oIdler.YieldTime ();
  510.     }
  511.     
  512.     if (!EndPointValid ())
  513.         lResult = keOTPEPaborted;
  514.  
  515.     return (OSErr) lResult;
  516. }
  517.  
  518. // Read and send data
  519.  
  520. void OTPollEndPoint::DoIdle(void)
  521. {
  522.     //Check and see if we've received a string
  523.     GetData();
  524.     
  525.     if (!mfLostConnection && !EndPointValid)
  526.     {
  527.         mfLostConnection = true;
  528.     }
  529.     
  530.     if (mfLostConnection&&(!mfDataWaiting)&&(!mfBytesReceived))
  531.     {
  532.         mfLostConnection = false;
  533.         HandleShutdown();
  534.     }
  535. }
  536.  
  537. //
  538. // Read data and pass it off to HandleData, called by DoIdle()
  539. //
  540. OSErr OTPollEndPoint::GetData ( void )
  541. {
  542.  
  543.     OSStatus    lResult = noErr;
  544.     OTFlags        flags;
  545.     OTResult    theResult;
  546.     Boolean        GotOne;
  547.     
  548.     short    index;
  549.     
  550.     //if (!EndPointValid ())
  551.     //    return -1;
  552.     if ((!mfDataWaiting)&&(!mfBytesReceived))
  553.         return noErr;
  554.     
  555.     if (mfDataWaiting)
  556.     {
  557.         theResult = OTRcv (mpEndpoint, (void*) (&mfInComingData[mfBytesReceived]),
  558.              sizeof(mfInComingData) - mfBytesReceived, &flags);
  559.         
  560.         if ((theResult < 0)&&(theResult != kOTNoDataErr))
  561.         {
  562.             CheckOTError (theResult);
  563.             return (OSErr) theResult;
  564.         }
  565.         else if (theResult > 0)
  566.         {
  567.             mfBytesReceived += theResult;
  568.             
  569.             if ((flags & T_MORE) == 0)
  570.             {
  571.                 mfDataWaiting = false;
  572.             }
  573.         }
  574.         else mfDataWaiting = false;
  575.     }
  576.         
  577.     if (!mfDataInIsText)    //Should we not be parsing this?
  578.     {
  579.         this->HandleData(mfInComingData, mfBytesReceived);    //have them handle it and clear it
  580.         mfBytesReceived = 0;
  581.     }
  582.     else
  583.     {
  584.         GotOne = false;
  585.         for (index = 0; index < mfBytesReceived; index++)
  586.         {
  587.             if (mfInComingData[index]==0x0A)    //found end of line, line feed of cr/lf
  588.             {
  589.                 GotOne = true;
  590.                 index++;    //convert the index into the size
  591.                 this->HandleData(mfInComingData, index);    //have them handle it and clear it
  592.                 BlockMove(&mfInComingData[index], &mfInComingData[0], mfBytesReceived-index);
  593.                 mfBytesReceived-=index;
  594.                 index = 0;
  595.             }
  596.         }
  597.         if (!GotOne)
  598.         {
  599.             for (index = 0; index < mfBytesReceived; index++)
  600.             {
  601.                 if (mfInComingData[index]==0x0D)    //Does this one have just returns?
  602.                 {
  603.                     GotOne = true;
  604.                     index++;    //convert the index into the size
  605.                     this->HandleData(mfInComingData, index);    //have them handle it and clear it
  606.                     BlockMove(&mfInComingData[index], &mfInComingData[0], mfBytesReceived-index);
  607.                     mfBytesReceived-=index;
  608.                     index = 0;
  609.                 }
  610.             }
  611.         }
  612.         if ((!GotOne)&&(mfBytesReceived>sizeof(mfInComingData)/2))    //it's just a whole lot with no line feed
  613.         {
  614.             this->HandleData(mfInComingData, mfBytesReceived);    //have them handle it and clear it
  615.             mfBytesReceived = 0;
  616.         }
  617.     }
  618.  
  619.     return noErr;
  620. }
  621.  
  622. //
  623. // Override this to get the data!
  624. //
  625. void OTPollEndPoint::HandleData (char* /*thestring*/, short /*numchars*/)
  626. {
  627.     //do nothing here, only here to be derived
  628. }
  629. //
  630. // If we need to tell someone we've closed, override this
  631. //
  632. void OTPollEndPoint::HandleShutdown    (void)
  633. {
  634.  
  635. }
  636.